#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"

#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
#include "esp_mac.h"
#endif

#include "esp_wifi.h"
#include "espnow_storage.h"
#include "espnow_utils.h"
#include "protocol_examples_common.h"
#include "example_common_private.h"
#include "espnow.h"
#include "espnow_ctrl.h"
#include "esp_now_hal.h"
#include "ch423.h"
#include "wifi_hal.h"

static const char *TAG = "esp_now_hal";

static SemaphoreHandle_t xESPNOWSemaphore;                              /* esp-now mutex */
const espnow_addr_t ESPNOW_ADDR = {0x1A, 0xAA, 0xBB, 0xCC, 0xDD, 0XF0}; /* set local device mac address */

typedef enum
{
    APP_ESPNOW_CTRL_INIT,
    APP_ESPNOW_CTRL_BOUND,
    APP_ESPNOW_CTRL_MAX
} app_espnow_ctrl_status_t;

static app_espnow_ctrl_status_t s_espnow_ctrl_status = APP_ESPNOW_CTRL_INIT;
static esp_now_hal_recv_data_t esp_now_hal_recv_panel;

static uint8_t primary = 0;
static wifi_second_chan_t second = 0;

/**
 * @brief esp_now_recv_handle
 *
 * @param src_addr
 * @param data
 * @param size
 * @param rx_ctrl
 * @return esp_err_t
 */
static esp_err_t esp_now_recv_handle(uint8_t *src_addr, void *data, size_t size, wifi_pkt_rx_ctrl_t *rx_ctrl)
{
    ESP_PARAM_CHECK(src_addr);
    ESP_PARAM_CHECK(data);
    ESP_PARAM_CHECK(size);
    ESP_PARAM_CHECK(rx_ctrl);

    static uint32_t count = 0;

    ESP_LOGI(TAG, "espnow_recv, <%" PRIu32 "> [" MACSTR "][%d][%d][%u]: %.*s", count++, MAC2STR(src_addr), rx_ctrl->channel, rx_ctrl->rssi, size, size, (char *)data);

    /* recv data format: [num]:(switch_statu) */

    /* check esp-now recv data format start */

    /* check ch423 data fomat start */
    if ((((char *)data)[0] == '[') && (((char *)data)[2] == ']') && (size == 5))
    {
        ESP_LOGI(TAG, "entry esp-now recv data handle.");
        /* handle esp-now rec data start */
        uint8_t switch_num = ((char *)data)[1] - '0';
        if (((char *)data)[4] != '0')
        {
            ch423_set_oc_level(switch_num, 1);
        }
        else
        {
            ch423_set_oc_level(switch_num, 0);
        }
        /* handle esp-now rec data end */

        /* ack esp-now data start */
        /* ack esp-now data end */
    }
    /* check ch423 data fomat end */

    /* check max30102 data format start */
    if ((strstr((char *)data, "SOP2") != NULL) && (strstr((char *)data, "HR") != NULL))
    {
        ESP_LOGI(TAG, "entry esp-now recv data handle.");
        sscanf((char *)data, "SOP2:%d,HR:%d", &esp_now_hal_recv_panel.max30102_panel.SOP2, &esp_now_hal_recv_panel.max30102_panel.HR);
        ESP_LOGI(TAG, "max30102_data:SOP2[%d]-HR[%d].", esp_now_hal_recv_panel.max30102_panel.SOP2, esp_now_hal_recv_panel.max30102_panel.HR);
    }
    /* check max30102 data format start */

    /* check esp-now recv data format end */

    return ESP_OK;
}

/**
 * @brief esp_now_electrical_open_num
 *
 * @param buff
 */
static void esp_now_electrical_open_num(char *buff)
{
    /* get open num start */
    uint8_t io_data = ch423_get_io_data();
    /* get open num end */

    if ((io_data & 0x01) == 0) /* OC_L bit0 */
        buff[0] = '0';         /* bit0 -> open */
    else
        buff[0] = '1'; /* bit0 -> close */

    if ((io_data & 0x02) == 0) /* OC_L bit1 */
        buff[1] = '0';         /* bit1 -> open */
    else
        buff[1] = '1'; /* bit1 -> close */

    if ((io_data & 0x04) == 0) /* OC_L bit2 */
        buff[2] = '0';         /* bit2 -> open */
    else
        buff[2] = '1'; /* bit2 -> close */

    if ((io_data & 0x08) == 0) /* OC_L bit3 */
        buff[3] = '0';         /* bit3 -> open */
    else
        buff[3] = '1'; /* bit3 -> close */

    if ((io_data & 0x10) == 0) /* OC_L bit0 */
        buff[4] = '0';         /* bit0 -> open */
    else
        buff[4] = '1'; /* bit0 -> close */

    if ((io_data & 0x20) == 0) /* OC_L bit0 */
        buff[5] = '0';         /* bit0 -> open */
    else
        buff[5] = '1'; /* bit0 -> close */

    if ((io_data & 0x40) == 0) /* OC_L bit0 */
        buff[6] = '0';         /* bit0 -> open */
    else
        buff[6] = '1'; /* bit0 -> close */

    if ((io_data & 0x80) == 0) /* OC_L bit0 */
        buff[7] = '0';         /* bit0 -> open */
    else
        buff[7] = '1'; /* bit0 -> close */

    buff[9] = ch423_get_open_num_data() + '0';
}

/**
 * @brief app_wifi_init
 *
 */
void app_wifi_init()
{
    ESP_ERROR_CHECK(esp_netif_init()); /* Initialize the underlying TCP/IP stack */

    esp_event_loop_create_default(); /* Create default event loop */

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();

    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
    ESP_ERROR_CHECK(esp_wifi_set_mac(ESP_IF_WIFI_STA, ESPNOW_ADDR)); /* set your device mac */
    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
    ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
    // ESP_ERROR_CHECK(esp_wifi_start());

    ESP_ERROR_CHECK(example_wifi_connect());
}

/**
 * @brief esp_now_mqtt_wifi_init
 *
 */
void esp_now_mqtt_wifi_init()
{
    /* espnow_storage_init(); */
    espnow_config_t espnow_config = ESPNOW_INIT_CONFIG_DEFAULT();
    app_wifi_init();
    ESP_ERROR_CHECK(espnow_init(&espnow_config));
    ESP_ERROR_CHECK(espnow_set_config_for_data_type(ESPNOW_DATA_TYPE_DATA, true, esp_now_recv_handle));

    // app_responder_init();
}

/**
 * @brief esp_now_get_channel
 *
 * @return uint8_t
 */
uint8_t esp_now_get_channel(void)
{
    return primary;
}

/**
 * @brief esp_now_get_max30102_data
 *
 * @param data_type
 * @return uint8_t
 */
uint8_t esp_now_get_max30102_data(max30102_type data_type)
{
    switch (data_type)
    {
    case SOP2:
        return esp_now_hal_recv_panel.max30102_panel.SOP2;
    case HR:
        return esp_now_hal_recv_panel.max30102_panel.HR;
    }
    return ESP_OK;
}

/**
 * @brief esp_now_task
 *
 * @param arg
 */
void esp_now_task(void *arg)
{
    esp_err_t ret = ESP_OK;

    /* esp-now mutex create start */
    xESPNOWSemaphore = xSemaphoreCreateMutex(); /* create esp-now mutex */
    if (xESPNOWSemaphore != NULL)
    {
        ESP_LOGI(TAG, "Creat xESPNOWSemaphore successfully");
    }
    else
    {
        /* error handler begin */
        ESP_LOGE(TAG, "Creat xESPNOWSemaphore error. err msg->%s,,%s", __FILE__, __func__);
        vSemaphoreDelete(xESPNOWSemaphore);
        /* error handler end */
    }
    /* esp-now mutex create end */

    uint32_t count = 0;

    espnow_frame_head_t frame_head = {
        .retransmit_count = CONFIG_RETRY_NUM,
        // .channel = CONFIG_ESPNOW_CHANNEL,
        .broadcast = true,
    };

    char esp_now_publish_data[11] = "11111111 0"; /* esp-now send data format */

    while (1)
    {
        /* get wifi and channel status start */
        esp_wifi_get_channel(&primary, &second);
        frame_head.channel = primary; /* update esp-now channel -> this must to equel wifi */
        /* get wifi and channel status end */

        /* update esp-now send channel start */

        /* update esp-now send channel end */

        if (wifi_panel().authmode == WIFI_AUTH_OPEN) /* ensure wifi is connect */
        {
            ESP_LOGW(TAG, "wifi channel is:[%d]. Haven't to connect to a suitable AP now!", primary);
        }
        else
        {
            // espnow_ctrl_initiator_send(ESPNOW_ATTRIBUTE_KEY_1, ESPNOW_ATTRIBUTE_POWER, 120);

            /* esp-now send switc data start */
            esp_now_electrical_open_num(esp_now_publish_data);
#if ESP_NOW_HAL_DEBUG
            ESP_LOGI(TAG, "ESPNOW_ADDR_BROADCAST:" MACSTR, MAC2STR(ESPNOW_ADDR_BROADCAST));
#endif
            ret = espnow_send(ESPNOW_DATA_TYPE_DATA, ESPNOW_ADDR_BROADCAST, esp_now_publish_data, sizeof(esp_now_publish_data), &frame_head, portMAX_DELAY);
            if (ret != ESP_OK)
            {
                ESP_LOGE(TAG, "espnow send err.[%d]", ret);
            }
            else
            {
                ESP_LOGI(TAG, "espnow_send, count: %" PRIu32 ", size: %u, data: %s", count++, sizeof(esp_now_publish_data), esp_now_publish_data);
            }
            /* esp-now send switc data end */

#if ESP_NOW_HAL_DEBUG
            ESP_LOGW(TAG, "esp-now channel:%d  second:%d", primary, second);
#endif
        }
        vTaskDelay(pdMS_TO_TICKS(1500));
    }
}
